package br.ufc.great.dtx.tm.lm;

import java.util.Stack;
import java.util.Vector;

import br.ufc.great.dataguide.DGDocument;
import br.ufc.great.dataguide.DGElement;
import br.ufc.great.dtx.server.DTXOutput;
import br.ufc.great.dtx.server.config.Config;
import br.ufc.great.dtx.tm.operations.InsertOperation;
import br.ufc.great.dtx.tm.operations.Operation;
import br.ufc.great.dtx.tm.operations.QueryOperation;
import br.ufc.great.xml.XMLAttribute;
import br.ufc.great.xml.XMLDocument;
import br.ufc.great.xml.XMLElement;
import br.ufc.great.xml.XMLNode;

/**
 * 
 * @author Leonardo Oliveira Moreira
 *
 * Classe que implementa o gerenciador de bloqueio processando as opera��es.
 */
public class LockManager {

	private XMLDocument document;
	private DGDocument dataGuide;

	private DTXOutput output;
	
	public static final int GRANULARITY_XDGL = 0;
	public static final int GRANULARITY_TREELOCK = 1;
	
	private LockTable lockTable;
	
	/**
	 * Construtor que configura o stream de sa�da as mensagens e o documento original a ser manipulado
	 * 
	 * @param output
	 * @param document
	 */
	public LockManager(DTXOutput output, XMLDocument document) {
		lockTable = new LockTable();
		this.output = output;
		setDocument(document);
	}
	
	/**
	 * Método utilizado para retornar o documento original
	 * 
	 * @return
	 */
	public XMLDocument getDocument() {
		return document;
	}
	
	/**
	 * Método utilizado para configurar o documeto original atualizando o DataGuide
	 *  
	 * @param document
	 */
	public void setDocument(XMLDocument document) {
		this.document = document;
		dataGuide = new DGDocument(document);
		XMLNode xmlRoot = document.getRoot();
		if (Config.GRANULARITY == GRANULARITY_XDGL) {
			output.print(getClass().getSimpleName(), "Creating DataGuide of (" + document.getName() + ")...", DTXOutput.MESSAGE);
			Vector<XMLElement> buffer = new Vector<XMLElement>();
			buffer.add(xmlRoot);
			createDataGuide(buffer, dataGuide.getRoot());
			output.print(getClass().getSimpleName(), "DataGuide (" + document.getName() + ") created", DTXOutput.MESSAGE);
		}
		else {
			output.print(getClass().getSimpleName(), "Creating Lock structure of (" + document.getName() + ")...", DTXOutput.MESSAGE);
			createDocument(xmlRoot, dataGuide.getRoot());
			output.print(getClass().getSimpleName(), "Lock structure (" + document.getName() + ") created", DTXOutput.MESSAGE);
		}
	}
	
	private void copyAttributesInElements(Vector<XMLElement> result, Vector<XMLAttribute> temp) {
		for (int i = 0; temp != null && i < temp.size(); i++)
			result.add(temp.elementAt(i));
	}

	private void copyNodesInElements(Vector<XMLElement> result, Vector<XMLNode> temp) {
		for (int i = 0; temp != null && i < temp.size(); i++)
			result.add(temp.elementAt(i));
	}
	
	private void createDocument(XMLElement element, DGElement dgElement) {
		DGElement dgCurrent = null;
		if (dgElement == null) {
			DGElement dgRoot = new DGElement(dataGuide.getNewId(), element.getName(), null);
			dgCurrent = dgRoot;
			dgCurrent.addIndex(element);
			dataGuide.setRoot(dgCurrent);
		}
		else {
			dgCurrent = new DGElement(dataGuide.getNewId(), element.getName(), dgElement);
			dgCurrent.addIndex(element);
			dgElement.addChildren(dgCurrent);				
		}
		if (element instanceof XMLNode) {
			Vector<XMLAttribute> attributes = ((XMLNode) element).getAttributes();
			for (int i = 0; attributes != null && i < attributes.size(); i++) {
				XMLAttribute item = attributes.elementAt(i);
				createDocument(item, dgCurrent);
			}
			Vector<XMLNode> nodes = ((XMLNode) element).getChildren();
			for (int i = 0; nodes != null && i < nodes.size(); i++) {
				XMLNode item = nodes.elementAt(i);
				createDocument(item, dgCurrent);
			}
		}	
	}
	
	private void createDataGuide(Vector<XMLElement> xmlElements, DGElement dgElement) {
		while (xmlElements.size() > 0) {
			Vector<XMLElement> buffer = new Vector<XMLElement>();
			XMLElement xmlCurrent = xmlElements.remove(0);
			DGElement dgCurrent = null;
			if (dgElement == null) {
				DGElement dgRoot = new DGElement(dataGuide.getNewId(), xmlCurrent.getName(), null);
				dgCurrent = dgRoot;
				dgCurrent.addIndex(xmlCurrent);
				dataGuide.setRoot(dgCurrent);
			}
			else {
				dgCurrent = new DGElement(dataGuide.getNewId(), xmlCurrent.getName(), dgElement);
				dgCurrent.addIndex(xmlCurrent);
				dgElement.addChildren(dgCurrent);				
			}
			if (xmlCurrent instanceof XMLAttribute) {
				for (int i = 0; i < xmlElements.size(); i++) {
					XMLElement item = xmlElements.elementAt(i);
					if (item instanceof XMLAttribute && item.getName().equalsIgnoreCase(xmlCurrent.getName())) {
						dgCurrent.addIndex(item);
						xmlElements.removeElementAt(i);
						i--;
					}
				}
			}
			else {
				if (xmlCurrent instanceof XMLNode) {
					copyAttributesInElements(buffer, ((XMLNode) xmlCurrent).getAttributes());
					copyNodesInElements(buffer, ((XMLNode) xmlCurrent).getChildren());
					for (int i = 0; i < xmlElements.size(); i++) {
						XMLElement item = xmlElements.elementAt(i);
						if (item instanceof XMLNode && item.getName().equalsIgnoreCase(xmlCurrent.getName())) {
							dgCurrent.addIndex(item);
							xmlElements.removeElementAt(i);
							i--;
							copyAttributesInElements(buffer, ((XMLNode) item).getAttributes());
							copyNodesInElements(buffer, ((XMLNode) item).getChildren());
						}
					}
				}
			}
			createDataGuide(buffer, dgCurrent);
		}
	}

	
	/**
	 * Método utilizado para retornar o DataGuide do documento original
	 * 
	 * @return
	 */
	public DGDocument getDataGuide() {
		return dataGuide;
	}
	
	/**
	 * Método que verifica e aplica um bloqueio a um elemento do DataGuide, caso o bloqueio seja incompat�vel a exce��o LockIncompatibleException � lan�ada.
	 * Dentro da exceção contém a transaão que gerou a incompatibilidade.
	 * 
	 * @param element
	 * @param transactionId
	 * @param operationId
	 * @param lockType
	 * @param pointers
	 * @throws LockIncompatibleException
	 */
	private void applyLocking(DGElement element, String transactionId, String operationId, long oid, int lockType) throws LockIncompatibleException {
		synchronized (lockTable) {
			lockTable.add(transactionId, operationId, element.getId(), lockType);
		}
	}
	
	/**
	 * Método utilizado para liberar os bloqueios dos elementos do DataGuide por uma transa��o.
	 * 
	 * @param pointers
	 * @param transactionId
	 */
	public void unlock(String transactionId) {
		synchronized (lockTable) {			
			lockTable.remove(transactionId);
			output.setNumberLocks(lockTable.size());						
		}
	}
	
	public void unlock(String transactionId, String operationId) {
		synchronized (lockTable) {			
			lockTable.remove(transactionId, operationId);
			output.setNumberLocks(lockTable.size());						
		}
	}
	
	/**
	 * Método utilizado para aplicar bloqueios recursivamente nos ascendentes de um elemento do DataGuide.
	 * Caso algum bloqueio seja incompat�vel a exce��o LockIncompatibleException � lan�ada contendo a transa��o conflitante.
	 * 
	 * @param element
	 * @param transactionId
	 * @param operationId
	 * @param lockType
	 * @param pointers
	 * @throws LockIncompatibleException
	 */
	private void applyLockingParents(DGElement element, String transactionId, String operationId, int[] lockType) throws LockIncompatibleException {
		if (element == null)
			return;
		for (int i = 0; i < lockType.length; i++) { 
			applyLocking(element, transactionId, operationId, element.getId(), lockType[i]);
			applyLockingParents(element.getParent(), transactionId, operationId, lockType);
		}
	}
	
	/**
	 * Método utilizado para processar uma opera��o no DataGuide. Este Método verifica o tipo de opera��o, processa e devolve o resultado da opera��o.
	 * Se durante a opera��o n�o for poss�vel obter algum bloqueio a opera��o � desfeita e a exce��o LockIncompatibleException � lan�ada.
	 * 
	 * @param operation
	 * @return
	 * @throws LockIncompatibleException
	 */
	public String processOperation(Operation operation) throws LockIncompatibleException {
		switch (operation.getType()) {
			/* Processa opera��o do tipo Query */
			case Operation.QUERY : {
				String[] nodes = extractNodes(((QueryOperation) operation).getQuery());
				XMLDocument result = new XMLDocument();
				if (nodes.length > 0) {
					XMLNode nodeResult = new XMLNode(0, null);
					nodeResult.setName("RESULT_QUERY");
					try {
						output.print(getClass().getSimpleName(), "selecting nodes", DTXOutput.TEST);
						synchronized (this) {
							if (Config.GRANULARITY == GRANULARITY_XDGL)
								selectNodes(nodeResult, dataGuide.getRoot(), nodes, 0, new Vector<XMLElement>(), operation);
							else
								selectNodesWithoutDataGuide(nodeResult, dataGuide.getRoot(), nodes, 0, new Vector<XMLElement>(), operation);
						}
						output.print(getClass().getSimpleName(), "selecting nodes ok", DTXOutput.TEST);
					}
					catch (LockIncompatibleException e) {
						output.print(getClass().getSimpleName(), e.getMessage(), DTXOutput.TEST);
						synchronized (lockTable) {
							output.setNumberLocks(lockTable.size());						
						}
						throw e;
					}
					result.setRoot(nodeResult);
					synchronized (lockTable) {
						output.setNumberLocks(lockTable.size());						
					}
				}
				return result.toString();
			}
			/* Processa opera��o do tipo Insert */
			case Operation.INSERT : {
				String[] nodes = extractNodes(((InsertOperation) operation).getQuery());
				if (nodes.length > 0) {
					XMLNode nodeResult = new XMLNode(0, null);
					try {
						output.print(getClass().getSimpleName(), "selecting nodes", DTXOutput.TEST);
						synchronized (this) {
							if (Config.GRANULARITY == GRANULARITY_XDGL)
								selectNodes(nodeResult, dataGuide.getRoot(), nodes, 0, new Vector<XMLElement>(), operation);
							else
								selectNodesWithoutDataGuide(nodeResult, dataGuide.getRoot(), nodes, 0, new Vector<XMLElement>(), operation);
						}
						output.print(getClass().getSimpleName(), "selecting nodes ok", DTXOutput.TEST);
					}
					catch (LockIncompatibleException e) {
						output.print(getClass().getSimpleName(), e.getMessage(), DTXOutput.TEST);
						synchronized (lockTable) {
							output.setNumberLocks(lockTable.size());						
						}
						throw e;
					}
					synchronized (lockTable) {
						output.setNumberLocks(lockTable.size());						
					}
				}
				String strResult = "<RESULT_INSERT>\n";
				if (((InsertOperation) operation).getConstructor() instanceof XMLAttribute) {
					strResult += "    <ATTRIBUTE NAME=\"" + ((XMLAttribute) ((InsertOperation) operation).getConstructor()).getName() + "\" VALUE=\"" + ((XMLAttribute) ((InsertOperation) operation).getConstructor()).getValue() + "\" />\n"; 
				}
				else {
					strResult += "    <NODE>\n";
					strResult += "        <" + ((XMLNode) ((InsertOperation) operation).getConstructor()).getName();
					for (int i = 0; ((XMLNode) ((InsertOperation) operation).getConstructor()).getAttributes() != null && i < ((XMLNode) ((InsertOperation) operation).getConstructor()).getAttributes().size(); i++) {
						strResult += " " + ((XMLNode) ((InsertOperation) operation).getConstructor()).getAttributes().get(i).getName() + "=\"" + ((XMLNode) ((InsertOperation) operation).getConstructor()).getAttributes().get(i).getValue() + "\"";
					}
					strResult += ">" + ((XMLNode) ((InsertOperation) operation).getConstructor()).getValue() + "</" + ((XMLNode) ((InsertOperation) operation).getConstructor()).getName() + ">\n";
					strResult += "    </NODE>\n";					
				}
				strResult += "    <INTO>" + ((InsertOperation) operation).getQuery() + "</INTO>\n";
				strResult += "</RESULT_INSERT>";
				return strResult;
			}
		}
		return null;
	}
	
	/**
	 * Método utilizado para percorrer e manipular elementos do DataGuide seguindo uma opera��o
	 * 
	 * @param result
	 * @param current
	 * @param path
	 * @param index
	 * @param lastIndex
	 * @param operation
	 * @param pointers
	 * @throws LockIncompatibleException
	 */
	private boolean selectNodes(XMLNode result, DGElement current, String[] path, int index, Vector<XMLElement> lastIndex, Operation operation) throws LockIncompatibleException {
		boolean stoped = false;
		if (index < path.length) {
			if (path[index].equalsIgnoreCase(current.getName())) {
				stoped = true;
				if (index == path.length - 1) {
					Vector<XMLElement> indexes = current.getIndexes();
					for (int i = 0; i < indexes.size(); i++) {
						if (lastIndex.size() == 0 || lastIndex.contains(indexes.elementAt(i).getParent())) {
							switch (operation.getType()) {
								case Operation.QUERY : {
									applyLocking(current, operation.getTransactionId(), operation.getId(), current.getId(), XDGLLockType.SHARED_TREE);
									applyLockingParents(current, operation.getTransactionId(), operation.getId(), new int[] { XDGLLockType.INTENTION_SHARED });
									result.addChildren((XMLNode) indexes.elementAt(i));
									break;
								}
								case Operation.INSERT : {
									applyLocking(current, operation.getTransactionId(), operation.getId(), current.getId(), XDGLLockType.SHARED_INTO);
									applyLockingParents(current, operation.getTransactionId(), operation.getId(), new int[] { XDGLLockType.INTENTION_SHARED, XDGLLockType.INTENTION_EXCLUSIVE });
									//insertElement((XMLNode) indexes.elementAt(i), current, (InsertOperation) operation);
									break;
								}
							}								
						}
					}
				}
				else {
					if (isFilter(path[index + 1])) {
						stoped = true;
						lastIndex = testFilter(path[index + 1], current, null, 0, null, current);
						if (index + 1 == path.length - 1) {
							Vector<XMLElement> indexes = current.getIndexes();
							for (int i = 0; i < indexes.size(); i++) {
								if (lastIndex.size() == 0 || lastIndex.contains(indexes.elementAt(i))) {
									switch (operation.getType()) {
										case Operation.QUERY : {
											applyLocking(current, operation.getTransactionId(), operation.getId(), current.getId(), XDGLLockType.SHARED_TREE);
											applyLockingParents(current, operation.getTransactionId(), operation.getId(), new int[] { XDGLLockType.INTENTION_SHARED });
											result.addChildren((XMLNode) indexes.elementAt(i));
											break;
										}
										case Operation.INSERT : {
											applyLocking(current, operation.getTransactionId(), operation.getId(), current.getId(), XDGLLockType.SHARED_TREE);
											applyLocking(current, operation.getTransactionId(), operation.getId(), current.getId(), XDGLLockType.SHARED_INTO);
											applyLocking(current, operation.getTransactionId(), operation.getId(), current.getId(), XDGLLockType.INTENTION_EXCLUSIVE);
											applyLockingParents(current, operation.getTransactionId(), operation.getId(), new int[] { XDGLLockType.INTENTION_SHARED, XDGLLockType.INTENTION_EXCLUSIVE });
											//insertElement((XMLNode) indexes.elementAt(i), current, (InsertOperation) operation);
											break;
										}
									}
								}
							}
						}
						else {
							Vector<DGElement> childrens = current.getChildrens();
							for (int i = 0; i < childrens.size(); i++)
								if (selectNodes(result, childrens.elementAt(i), path, index + 2, lastIndex, operation))
									break;
						}
					}
					else {
						Vector<DGElement> childrens = current.getChildrens();
						for (int i = 0; i < childrens.size(); i++)
							if (selectNodes(result, childrens.elementAt(i), path, index + 1, lastIndex, operation))
								break;
					}
				}
			}
		}
		return stoped;
	}

	private void selectNodesWithoutDataGuide(XMLNode result, DGElement current, String[] path, int index, Vector<XMLElement> lastIndex, Operation operation) throws LockIncompatibleException {
		//output.print(getClass().getSimpleName(), "entrou", DTXOutput.TEST);
		if (index < path.length) {
			if (path[index].equalsIgnoreCase(current.getName())) {
				if (index == path.length - 1) {
					Vector<XMLElement> indexes = current.getIndexes();
					for (int i = 0; i < indexes.size(); i++) {
						switch (operation.getType()) {
							case Operation.QUERY : {
								if (current.getParent() != null) {
									applyLockingParents(current.getParent(), operation.getTransactionId(), operation.getId(), new int[] { XDGLLockType.SHARED_DOCUMENT });
									applyLockingDescendents(current.getParent(), operation.getTransactionId(), operation.getId(), new int[] { XDGLLockType.SHARED_DOCUMENT });
								}
								else {
									applyLockingParents(current, operation.getTransactionId(), operation.getId(), new int[] { XDGLLockType.SHARED_DOCUMENT });
									applyLockingDescendents(current, operation.getTransactionId(), operation.getId(), new int[] { XDGLLockType.SHARED_DOCUMENT });
								}
								result.addChildren((XMLNode) indexes.elementAt(i));
								break;
							}
							case Operation.INSERT : {
								if (current.getParent() != null) {
									applyLockingParents(current.getParent(), operation.getTransactionId(), operation.getId(), new int[] { XDGLLockType.EXCLUSIVE_DOCUMENT });
									applyLockingDescendents(current.getParent(), operation.getTransactionId(), operation.getId(), new int[] { XDGLLockType.EXCLUSIVE_DOCUMENT });
								}
								else {
									applyLockingParents(current, operation.getTransactionId(), operation.getId(), new int[] { XDGLLockType.EXCLUSIVE_DOCUMENT });
									applyLockingDescendents(current, operation.getTransactionId(), operation.getId(), new int[] { XDGLLockType.EXCLUSIVE_DOCUMENT });
								}
								//insertElement((XMLNode) indexes.elementAt(i), current, (InsertOperation) operation);
								break;
							}
						}
					}
				}
				else {
					if (isFilter(path[index + 1])) {
						boolean test = testFilterWithoutDataGuide(path[index + 1], current, null, 0, null, current);
						if (index + 1 == path.length - 1) {
							Vector<XMLElement> indexes = current.getIndexes();
							for (int i = 0; test && i < indexes.size(); i++) {
								switch (operation.getType()) {
									case Operation.QUERY : {
										if (current.getParent() != null) {
											applyLockingParents(current.getParent(), operation.getTransactionId(), operation.getId(), new int[] { XDGLLockType.SHARED_DOCUMENT });
											applyLockingDescendents(current.getParent(), operation.getTransactionId(), operation.getId(), new int[] { XDGLLockType.SHARED_DOCUMENT });
										}
										else {
											applyLockingParents(current, operation.getTransactionId(), operation.getId(), new int[] { XDGLLockType.SHARED_DOCUMENT });
											applyLockingDescendents(current, operation.getTransactionId(), operation.getId(), new int[] { XDGLLockType.SHARED_DOCUMENT });
										}
										result.addChildren((XMLNode) indexes.elementAt(i));
										break;
									}
									case Operation.INSERT : {
										if (current.getParent() != null) {
											applyLockingParents(current.getParent(), operation.getTransactionId(), operation.getId(), new int[] { XDGLLockType.EXCLUSIVE_DOCUMENT });
											applyLockingDescendents(current.getParent(), operation.getTransactionId(), operation.getId(), new int[] { XDGLLockType.EXCLUSIVE_DOCUMENT });
										}
										else {
											applyLockingParents(current, operation.getTransactionId(), operation.getId(), new int[] { XDGLLockType.EXCLUSIVE_DOCUMENT });
											applyLockingDescendents(current, operation.getTransactionId(), operation.getId(), new int[] { XDGLLockType.EXCLUSIVE_DOCUMENT });
										}
										//insertElement((XMLNode) indexes.elementAt(i), current, (InsertOperation) operation);
										break;
									}
								}
							}
						}
						else {
							Vector<DGElement> childrens = current.getChildrens();
							for (int i = 0; test && i < childrens.size(); i++)
								selectNodesWithoutDataGuide(result, childrens.elementAt(i), path, index + 2, lastIndex, operation);
						}
					}
					else {
						Vector<DGElement> childrens = current.getChildrens();
						for (int i = 0; i < childrens.size(); i++) {
							selectNodesWithoutDataGuide(result, childrens.elementAt(i), path, index + 1, lastIndex, operation);
						}
					}
				}
			}
		}
		//output.print(getClass().getSimpleName(), "saiu", DTXOutput.TEST);
	}
	
	/**
	 * Método utilizado para verificar a validade e percorrer predicados de uma consulta XPath pelo DataGuide
	 * 
	 * @param filter
	 * @param parent
	 * @param nodes
	 * @param index
	 * @param value
	 * @param current
	 * @return
	 */
	private Vector<XMLElement> testFilter(String filter, DGElement parent, String[] nodes, int index, String value, DGElement current) {
		Vector<XMLElement> result = new Vector<XMLElement>();
		if (nodes != null && nodes.length > 0 && index < nodes.length) {
			if (isFilter(nodes[index])) {
				filter = nodes[index];
				if (result.size() == 0)
					result = testFilterString(filter, parent, nodes, index, value, current);
			}
			else {
				if (nodes[index].startsWith("@")) {
					Vector<DGElement> attributes = parent.getChildrens();
					for (int i = 0; attributes != null && i < attributes.size(); i++) {
						DGElement attributeItem = attributes.get(i);
						if (("@" + attributeItem.getName()).equalsIgnoreCase(nodes[index])) {
							Vector<XMLElement> indexes = attributeItem.getIndexes();
							for (int j = 0; j < indexes.size(); j++) {
								XMLElement item = indexes.elementAt(j);
								if (item.getValue().equalsIgnoreCase(value)) {
									result.add(item);
								}
							}
						}
					}
				}
				else {
					Vector<DGElement> childrens = parent.getChildrens();
					for (int i = 0; childrens != null && i < childrens.size(); i++) {
						DGElement children = childrens.get(i);
						if (index == nodes.length - 1 && value != null) {
							Vector<XMLElement> indexes = children.getIndexes();
							for (int j = 0; j < indexes.size(); j++) {
								XMLElement item = indexes.elementAt(j);
								if (children.getName().equalsIgnoreCase(nodes[index]) && item.getValue().equalsIgnoreCase(value)) {
									result.add(item);
								}
							}
						}
						else {
							if (result.size() == 0)
								result = testFilter(filter, children, nodes, index + 1, value, current);
						}
							
					}
				}
			}
		}
		else {
			if (result.size() == 0)
				result = testFilterString(filter, parent, nodes, index, value, current);
		}
		Vector<XMLElement> indexes = current.getIndexes();
		for (int i = 0; i < indexes.size(); i++)
			if (result.contains(indexes.elementAt(i)))
				return result;
		if (result.size() > 0) {
			for (int i = 0; i < result.size(); i++) {
				XMLElement temp = result.elementAt(i);
				result.set(i, temp.getParent());
			}
			return result;
		}
		else
			return result;
	}
	
	private boolean testFilterWithoutDataGuide(String filter, DGElement parent, String[] nodes, int index, String value, DGElement current) {
		boolean result = false;
		if (nodes != null && nodes.length > 0 && index < nodes.length) {
			if (isFilter(nodes[index])) {
				filter = nodes[index];
				if (! result)
					result = testFilterStringWithoutDataGuide(filter, parent, nodes, index, value, current);
			}
			else {
				if (nodes[index].startsWith("@")) {
					Vector<DGElement> attributes = parent.getChildrens();
					for (int i = 0; attributes != null && i < attributes.size(); i++) {
						DGElement attributeItem = attributes.get(i);
						if (("@" + attributeItem.getName()).equalsIgnoreCase(nodes[index])) {
							Vector<XMLElement> indexes = attributeItem.getIndexes();
							for (int j = 0; j < indexes.size(); j++) {
								XMLElement item = indexes.elementAt(j);
								if (item.getValue().equalsIgnoreCase(value)) {
									result = true;
								}
							}
						}
					}
				}
				else {
					Vector<DGElement> childrens = parent.getChildrens();
					for (int i = 0; childrens != null && i < childrens.size(); i++) {
						DGElement children = childrens.get(i);
						if (index == nodes.length - 1 && value != null) {
							Vector<XMLElement> indexes = children.getIndexes();
							for (int j = 0; j < indexes.size(); j++) {
								XMLElement item = indexes.elementAt(j);
								if (children.getName().equalsIgnoreCase(nodes[index]) && item.getValue().equalsIgnoreCase(value)) {
									result = true;
								}
							}
						}
						else {
							if (! result)
								result = testFilterStringWithoutDataGuide(filter, children, nodes, index + 1, value, current);
						}
							
					}
				}
			}
		}
		else {
			if (! result)
				result = testFilterStringWithoutDataGuide(filter, parent, nodes, index, value, current);
		}
		return result;
	}

	/**
	 * Método utilizado para analizar e fragmentar string de predicados de consulta XPath pelo DataGuide
	 * 
	 * @param filter
	 * @param parent
	 * @param nodes
	 * @param index
	 * @param value
	 * @param current
	 * @return
	 */
	private Vector<XMLElement> testFilterString(String filter, DGElement parent, String[] nodes, int index, String value, DGElement current) {
		Vector<XMLElement> result = new Vector<XMLElement>();
		if (filter.startsWith("[") && filter.endsWith("]")) {
			filter = filter.substring(1, filter.length() - 1);
			result = testFilter(filter, parent, null, 0, null, current);
		}
		else {
			if (filter.indexOf("[") > -1) {
				nodes = extractNodes(filter);
				filter = filter.substring(filter.indexOf("["));
				result = testFilter(filter, parent, nodes, 0, null, current);
			}
			else {
				if (filter.indexOf("=") > -1) {
					nodes = extractNodes(filter.substring(0, filter.indexOf("=")));
					value = filter.substring(filter.indexOf("=") + 1);
					filter = "";
					result = testFilter(filter, parent, nodes, 0, value, current);
				}
			}
		}
		return result;
	}
	
	private boolean testFilterStringWithoutDataGuide(String filter, DGElement parent, String[] nodes, int index, String value, DGElement current) {
		boolean result = false;
		if (filter.startsWith("[") && filter.endsWith("]")) {
			filter = filter.substring(1, filter.length() - 1);
			result = testFilterWithoutDataGuide(filter, parent, null, 0, null, current);
		}
		else {
			if (filter.indexOf("[") > -1) {
				nodes = extractNodes(filter);
				filter = filter.substring(filter.indexOf("["));
				result = testFilterWithoutDataGuide(filter, parent, nodes, 0, null, current);
			}
			else {
				if (filter.indexOf("=") > -1) {
					nodes = extractNodes(filter.substring(0, filter.indexOf("=")));
					value = filter.substring(filter.indexOf("=") + 1);
					filter = "";
					result = testFilterWithoutDataGuide(filter, parent, nodes, 0, value, current);
				}
			}
		}
		return result;
	}

	/**
	 * Método utilizado para inserir um elemento XML da opera��o de inser��o no nodo corrente.
	 * 
	 * @param current
	 * @param operation
	 * @throws LockIncompatibleException 
	 */
	private void insertElement(XMLNode result, DGElement current, InsertOperation operation) throws LockIncompatibleException {
		XMLElement element = operation.getConstructor();
		element.setId(document.getNewId());
		element.setParent(result);
		if (element instanceof XMLNode) {
			for (int i = 0; i < ((XMLNode) element).getAttributes().size(); i++) {
				XMLAttribute attr = ((XMLNode) element).getAttributes().elementAt(i);
				attr.setId(document.getNewId());
				attr.setParent((XMLNode) element);
			}
			//criar o DataGuide do element
			Vector<DGElement> currentChildrens = current.getChildrens();
			for (int i = 0; i < currentChildrens.size(); i++) {
				DGElement item = currentChildrens.elementAt(i);
				applyLocking(item, operation.getTransactionId(), operation.getId(), item.getId(), XDGLLockType.EXCLUSIVE);
				if (item.getName().equalsIgnoreCase(element.getName())) {
					item.addIndex(element);
					Vector<DGElement> itemChilds = item.getChildrens();
					for (int j = 0; j < ((XMLNode) element).getAttributes().size(); j++) {
						XMLAttribute attr = ((XMLNode) element).getAttributes().elementAt(j);
						for (int k = 0; k < itemChilds.size(); k++)
							if (itemChilds.elementAt(k).getName().equalsIgnoreCase(attr.getName()))
								itemChilds.elementAt(k).addIndex(attr);
					}
					break;
				}
			}
			//---
			applyLocking(current, operation.getTransactionId(), operation.getId(), current.getId(), XDGLLockType.EXCLUSIVE);
			result.addChildren((XMLNode) element);
		}
		else {
			Vector<DGElement> currentChildrens = current.getChildrens();
			for (int i = 0; i < currentChildrens.size(); i++) {
				DGElement item = currentChildrens.elementAt(i);
				applyLocking(item, operation.getTransactionId(), operation.getId(), current.getId(), XDGLLockType.EXCLUSIVE);
				if (item.getName().equalsIgnoreCase(element.getName())) {
					item.addIndex(element);
					break;
				}
			}
			applyLocking(current, operation.getTransactionId(), operation.getId(), current.getId(), XDGLLockType.EXCLUSIVE);
			Vector<XMLAttribute> attributes = result.getAttributes();
			attributes.add((XMLAttribute) element);
		}
	}

	/**
	 * Método utilizado para verificar se o nodo corrente cont�m filtro.
	 * 
	 * @param currentNode
	 * @return
	 */
	private boolean isFilter(String currentNode) {
		return currentNode.indexOf("[") > -1;
	}

	/**
	 * Método utilizado para extrair nodos do n�vel corrente.
	 * 
	 * @param string
	 * @return
	 */
	private String[] extractNodes(String string) {
		Vector<String> vNodes = new Vector<String>();
		int i = 0;
		String buffer = "";
		while (i < string.length()) {
			String chr = string.substring(i, i + 1);
			if (chr.equalsIgnoreCase("/")) {
				if (buffer.trim().length() > 0) {
					vNodes.add(buffer);
					buffer = "";
				}
				i++;
			}
			else {
				if (chr.equalsIgnoreCase("[")) {
					if (buffer.trim().length() > 0) {
						vNodes.add(buffer);
						buffer = "";
					}					
					Stack<String> stack = new Stack<String>();
					stack.push("[");
					buffer += chr;
					i++;
					while (! stack.isEmpty()) {
						chr = string.substring(i, i + 1);
						if (chr.equalsIgnoreCase("["))
							stack.push("[");
						else
							if (chr.equalsIgnoreCase("]"))
								stack.pop();
						buffer += chr;
						i++;
					}
					if (buffer.trim().length() > 0) {
						vNodes.add(buffer);
						buffer = "";
					}
				}
				else {
					buffer += chr;
					i++;
					if (i == string.length())
						if (buffer.trim().length() > 0) {
							vNodes.add(buffer);
							buffer = "";
						}
				}
			}
		}
		String[] nodes = new String[vNodes.size()];
		for (i = 0; i < nodes.length; i++) {
			nodes[i] = (String) vNodes.elementAt(i);
		}
		return nodes;
	}
	
	/*
	public String getXMLDocument(XMLDocument document, String transactionId) {
		String strDocument = "";
		strDocument = printElement(document.getRoot(), 1, transactionId);
		return strDocument;
	}
	*/
	/**
	 * Método utilizado para imprimir um elemento do DataGuide
	 * 
	 * @param current
	 * @param level
	 * @return
	 */
	/*
	private String printElement(XMLNode current, int level, String transactionId) {
		String temp = "";
		if (isVisible(transactionId, current)) {
			for (int i = 0; level != 1 && i < (level * 2); i++)
				temp += " ";
			temp += "<" + current.getName();
			String strAttributes = "";
			Vector<XMLAttribute> attributes = current.getAttributes();
			for (int i = 0; attributes != null && i < attributes.size(); i++){
				XMLAttribute attribute = attributes.elementAt(i);
				if (isVisible(transactionId, attribute))
					strAttributes += " " + attribute.getName() + "=\"" + attribute.getValue() + "\"";
			}
			temp += strAttributes;
			Vector<XMLNode> childrens = current.getChildren();
			if (childrens != null && childrens.size() > 0) {
				temp += ">\n";
				for (int i = 0; i < childrens.size(); i++) {
					XMLNode child = childrens.elementAt(i);
					temp += printElement(child, level + 1, transactionId);
				}
				for (int i = 0; level != 1 && i < (level * 2); i++)
					temp += " ";
				temp += "</" + current.getName()+ ">\n";			
			}
			else
				if (current.getValue() != null && current.getValue().trim().length() > 0)
					temp += ">" + current.getValue().trim() + "</" + current.getName()+ ">\n";
				else
					temp += " />\n";
		}
		return temp;
	}
	*/
	/**
	 * Método utilizado para testar se um elemento � vis�vel para uma transa��o
	 * 
	 * @param transactionId
	 * @param element
	 * @return
	 */
	/*
	private boolean isVisible(String transactionId, XMLElement element) {
		boolean result = true;
		Enumeration keys = uncommitedElements.keys();
		while (keys.hasMoreElements()) {
			String key = (String) keys.nextElement();
			if (key.equalsIgnoreCase(transactionId))
				continue;
			else {
				Vector<XMLElement> elements = uncommitedElements.get(key);
				if (elements.contains(element)) {
					result = false;
					break;
				}
			}
		}
		return result;
	}
	*/

	private void applyLockingDescendents(DGElement element, String transactionId, String operationId, int[] lockType) throws LockIncompatibleException {
		if (element == null)
			return;
		for (int i = 0; i < lockType.length; i++) { 
			applyLocking(element, transactionId, operationId, element.getId(), lockType[i]);
			Vector<DGElement> childrens = element.getChildrens();
			for (int j = 0; childrens != null && j < childrens.size(); j++)
				applyLockingDescendents(childrens.elementAt(j), transactionId, operationId, lockType);
		}
	}

}
